package pl.krakow.ae.knp.wikiboard.gui.board;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

import pl.krakow.ae.knp.wikiboard.communication.Connection;
import pl.krakow.ae.knp.wikiboard.gui.drawables.Drawable;
import pl.krakow.ae.knp.wikiboard.gui.tools.Tool;
import pl.krakow.ae.knp.wikiboard.gui.tools.ToolType;
import pl.krakow.ae.knp.wikiboard.gui.tools.ToolsFactory;


/**
 * Class that represents the main painting board.
 *
 */
public class Board implements BoardEventListener {
    private BoardView boardView;
    
    public static List<Drawable> drawablesList;
    public static LinkedList<BoardEvent> events;
    public static LinkedList<BoardEvent> redoEvents;
    private Tool currentTool;
    private Drawable currentDrawable;
    
    private static Color bgColor, fgColor, selectColor;
    
    private static final short BORDER_WIDTH = 1;
    private static final Color INITIAL_FG_COLOR = Color.BLACK,
            INITIAL_BG_COLOR = Color.WHITE,
            INITIAL_SELECT_COLOR = Color.BLUE;
    
    private static final int BOARD_WIDTH = 600,
            BOARD_HEIGHT = 400;
    
    private  Connection c;
    
    
    /**
     * Creates a new instance of Board
     *
     */
    public Board() {
        boardView = new BoardView(new Dimension(BOARD_WIDTH, BOARD_HEIGHT));
        
        currentTool = ToolsFactory.createTool(ToolType.SELECTOR);
        currentDrawable = null;
        
        drawablesList = new LinkedList<Drawable>();
        events = new LinkedList<BoardEvent>();
        redoEvents = new LinkedList<BoardEvent>();
        
        setFgColor(INITIAL_FG_COLOR);
        setBgColor(INITIAL_BG_COLOR);
        setSelectColor(INITIAL_SELECT_COLOR);
        
        currentTool.addBoardEventListener(this);
        
//        Connection.addBoardEventListener(this);
    }
    
    public void setTool(ToolType t) {
        currentTool = ToolsFactory.createTool(t);
        currentTool.addBoardEventListener(this);
        boardView.setCursor(t.getCursor());
    }
    
    /** Performs undo operation on board. Nothing is done when there
     * are no previous events to be undone. */
    public void undo() {
        if (!events.isEmpty()) {
            redoEvents.push(events.pop());
            playEvents();
        }
    }
    
    /** Performs redo operation on board. Nothing is done when there
     * are no events to be redone. */
    public void redo() {
        if (!redoEvents.isEmpty()) {
            events.push(redoEvents.pop());
            executeEvent(events.peek());
        }
    }
    
    /** Playing all the events from the start. */
    private void playEvents() {
        drawablesList.clear();
        
        Iterator<BoardEvent> it = events.descendingIterator();
        while(it.hasNext())
            executeEvent(it.next());
    }
    
    /** Executes given event.
     * @param event event to be executed. */
    private void executeEvent(BoardEvent event) {
        if (event.removedDrawable != null)
            drawablesList.remove(event.removedDrawable);
        if (event.addedDrawable != null)
            drawablesList.add(event.addedDrawable);
    }
    
    
    public static Color getSelectColor() {
        return selectColor; }
    
    public static Color getBgColor() {
        return bgColor; }
    
    public static Color getFgColor() {
        return fgColor; }
    
    public void setBgColor(Color aBgColor) {
        bgColor = aBgColor; }
    
    public void setFgColor(Color aFgColor) {
        fgColor = aFgColor; }
    
    public void setSelectColor(Color aSelectColor) {
        selectColor = aSelectColor; }
    
    /** A method that clears all drawables and events. */
    public void clearTable() {
        events.clear();
        redoEvents.clear();
        playEvents();
    }
    
    /** Called by a Tool whenever event occured.
     * @param event event which occured. */
    public void boardModified(BoardEvent event) {
        
        events.push(event);
        executeEvent(event);
        
//        Connection.send(event);
        
        System.out.println("Number of drawed objects: " + drawablesList.size());
    }
    
    /** A method that should be called by GUI if it wants the BoardView
     * to be repainted. */
    public void repaint() {
        boardView.repaint(); }
    
    /** A method that returns BoardView.
     * @return current BoardView. */
    public BoardView getBoardView() {
        return boardView; }
    
    /*********************************************************************
     *********************************************************************/
    
    /** A class that is responsible for graphical presentation
     * of the Board. */
    
    class BoardView extends JPanel implements MouseListener, MouseMotionListener {
        private Dimension size;
        
        /** Creates new BoardView instance.
         * @param size initial size of the BoardView. */
        BoardView(Dimension size) {
            super();
            this.size=size;
            
            setMinimumSize(size);
            setPreferredSize(size);
            setSize(size);
            setBorder(BorderFactory.createLineBorder(Color.BLACK));
            
            addMouseListener(this);
            addMouseMotionListener(this);
        }
        
        private void initCanvas(Graphics2D g) {
            g.setColor(bgColor);
            g.fillRect(BORDER_WIDTH, BORDER_WIDTH,
                    getWidth()-2*BORDER_WIDTH,
                    getHeight()-2*BORDER_WIDTH);
        }
        
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            paintAll((Graphics2D) g);
        }
        
        public void paintAll(Graphics2D g) {
            initCanvas(g);
            Iterator<Drawable> it = drawablesList.iterator();
            while(it.hasNext())
                it.next().draw(g, false);
            
        }
        
        public void paintAll() {
            paintAll((Graphics2D) getGraphics()); }
        
        public void mousePressed(MouseEvent event) {
            Graphics2D g = (Graphics2D) getGraphics();
            currentTool.mousePressed(event, g);
            currentDrawable = currentTool.getDrawable();
        }
        
        public void mouseReleased(MouseEvent event) {
            Graphics2D g = (Graphics2D) getGraphics();
            currentTool.mouseReleased(event, g);
            redoEvents.clear();
            repaint();
        }
        
        public void mouseDragged(MouseEvent event) {
            Graphics2D g = (Graphics2D) getGraphics();
            currentTool.mouseDragged(event, g);
        }
        
        public void mouseMoved(MouseEvent event) {
        }
        public void mouseClicked(MouseEvent event) {
        }
        public void mouseEntered(MouseEvent event) {
        }
        public void mouseExited(MouseEvent event) {
        }
        
        
    }
}
